package top.hjcwzx.wrench.limiter.aop;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.Ordered;
import top.hjcwzx.wrench.limiter.exception.WrenchKitLimterException;
import top.hjcwzx.wrench.limiter.annotation.IpRateLimiter;
import top.hjcwzx.wrench.limiter.config.RateLimiterIpListAutoProperties;
import top.hjcwzx.wrench.limiter.strategy.algorithm.RateLimiterAlgorithmExecutor;
import top.hjcwzx.wrench.limiter.utils.IpUtil;

@Aspect
public class IpRateLimiterAspect extends AbstractCommonLimiter implements Ordered {

    Logger logger = LoggerFactory.getLogger(IpRateLimiterAspect.class);

    @Autowired
    private RateLimiterAlgorithmExecutor algorithmExecutor;
    @Autowired
    private RateLimiterIpListAutoProperties ipListAutoProperties;


    @Pointcut("@annotation(top.hjcwzx.wrench.limiter.annotation.IpRateLimiter)")
    public void aopPoint() {
    }

    @Around("aopPoint() && @annotation(rateLimiter)")
    public Object around(ProceedingJoinPoint pjp, IpRateLimiter rateLimiter) throws Throwable {


        // 1. 获取 IP
        String ip = IpUtil.getClientIp();
        logger.debug("wrench-kit工具,ip限流管理,ip=" + ip);

        // 2. 黑白名单校验

        if (ipListAutoProperties.inWhiteList(ip)) {
            //白名单放行
            return pjp.proceed();
        }

        if (ipListAutoProperties.inBlackList(ip)) {
            throw new WrenchKitLimterException("ip in ipBlackList");
        }

        //3. 执行限流判断
        boolean allowed = algorithmExecutor.tryAccess(ip, rateLimiter.permitsPerSecond(), rateLimiter.algorithmStrategy());

        if (!allowed) {
            if (!rateLimiter.fallbackMethod().isEmpty()) {
                try {
                    return commonFallBack(pjp, rateLimiter.fallbackBean(), rateLimiter.fallbackMethod());
                } catch (Exception ex) {
                    logger.error("fallback error", ex);
                    throw new WrenchKitLimterException("limit used，but fallback error");
                }
            }
            throw new WrenchKitLimterException("key limit access");
        }

        return pjp.proceed();
    }

    @Override
    public int getOrder() {
        return 1;
    }
}
